package com.gonsin.gmessage.logic.proxy.receiver;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.gonsin.gmessage.annotations.GMessageEnvironment;
import com.gonsin.gmessage.annotations.GMessageReceiver;
import com.gonsin.gmessage.annotations.GMessageReceivers;
import com.gonsin.gmessage.annotations.GZoom;
import com.gonsin.gmessage.logic.model.GMessageModel;
import com.gonsin.gmessage.logic.model.ReceiveMessageModel;
import com.gonsin.gmessage.logic.mqtt.MqttClientManager;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerEndpointRegistry;
import org.springframework.jms.config.MethodJmsListenerEndpoint;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Configuration
public class GMessageReceiverRegister implements BeanPostProcessor {

    // 防止重复注入
    private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));

//    private BeanFactory beanFactory;

    private Map<String, Map<String, MessageContainer>> containerMap= new ConcurrentHashMap<>();

    @Autowired private MqttClientManager mqttClientManager;


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {


//        if(GMessageEnvironment.LOG){
//            log.info("GMessageInitRegister 开始注入 @GMessageReceiver ");
//        }
//        return bean;

        if (bean instanceof AopInfrastructureBean) {
            // Ignore AOP infrastructure such as scoped proxies.
            return bean;
        }

        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
        if (!this.nonAnnotatedClasses.contains(targetClass)) {
            Map<Method, Set<GMessageReceiver>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
                    (MethodIntrospector.MetadataLookup<Set<GMessageReceiver>>) method -> {
                        Set<GMessageReceiver> listenerMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
                                method, GMessageReceiver.class, GMessageReceivers.class);
                        return (!listenerMethods.isEmpty() ? listenerMethods : null);
                    });
            if (annotatedMethods.isEmpty()) {
                this.nonAnnotatedClasses.add(targetClass);
//                if(GMessageEnvironment.LOG){
//                    log.info("没有找到 @GMessageReceiver");
//                }
            } else {
                if(GMessageEnvironment.LOG){
                    log.info("开始注册 @GMessageReceiver 在 {} {}", beanName, annotatedMethods);
                }
                // Non-empty set of methods
                annotatedMethods.forEach((method, listeners) ->
                        listeners.forEach(listener -> processGMessageReceiver(listener, method, bean)));
            }
        }
        return bean;
    }

    /**
     * 将GMessageReceiver方法注册到spring bean中
     * @param receiver
     * @param mostSpecificMethod
     * @param bean
     */
    protected void processGMessageReceiver(GMessageReceiver receiver, Method mostSpecificMethod, Object bean) {
        Method invocableMethod = AopUtils.selectInvocableMethod(mostSpecificMethod, bean.getClass());

        GMessageReceiverEndpoint endpoint = new GMessageReceiverEndpoint();
        endpoint.setBean(bean);
        endpoint.setMethod(invocableMethod);
        endpoint.setFirstCmd(receiver.firstCmd());
        endpoint.setSubCmd(receiver.subCmd());
        endpoint.setQos(receiver.qos());
        endpoint.setShare(receiver.share());
        endpoint.setHasZoom(hasZoom(mostSpecificMethod));

        this.registerListenerContainer(endpoint);
    }

    /**
     * 判断方法里是否有@GZoom字段
     * @param mostSpecificMethod
     * @return
     */
    private boolean hasZoom(Method mostSpecificMethod) {
        Parameter[] parameters = mostSpecificMethod.getParameters();
        for(Parameter p : parameters){
            GZoom z = p.getDeclaredAnnotation(GZoom.class);
            if(z != null){
                return true;
            }
        }
        return false;
    }

//    @Override
//    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
//        this.beanFactory = beanFactory;
//    }

    /**
     * 将接收事件注册到单例
     */
    public void registerListenerContainer(GMessageReceiverEndpoint endpoint) {
        String firstCmd = endpoint.getFirstCmd();
        String subCmd = endpoint.getSubCmd();
        String topic = (endpoint.isShare() ? "$SHARE/" : "") + "venus/" + firstCmd + "/" + subCmd;

        // 如果含有zoom，则最后一位用通配符表示
        topic += endpoint.isHasZoom() ? "/#" : "";
        mqttClientManager.subscribe(topic, endpoint.getQos(), endpoint);

//        Map<String, MessageContainer> map = containerMap.computeIfAbsent(firstCmd, f -> new ConcurrentHashMap<>());
//        MessageContainer container = map.computeIfAbsent(subCmd, s -> new MessageContainer());
//        container.addCallback(endpoint);
    }

    /**
     * 处理收到的消息
     */
    public void notifyMessageReceive(String topic, String payload) {
        String[] cmds = topic.split("/");
        if(cmds == null || cmds.length < 3){
            if(GMessageEnvironment.LOG){
                log.warn("收到无效topic=【{}】，payload=【{}】", topic, payload);
            }
            return;
        }

        String firstCmd = cmds[1];
        String subCmd = cmds[2];
        String zoom = cmds.length > 3 ? cmds[3] : null;

        Map<String, MessageContainer> map2 = containerMap.get(firstCmd);
        if(map2 == null){
            return;
        }

        MessageContainer container = map2.get(subCmd);
        if(container == null){
            return;
        }

        GMessageModel message = JSONObject.parseObject(payload, GMessageModel.class);
        message.setZoom(zoom);
        container.notifyMessageReceive(message);
    }

}
